package com.atlassian.bamboo.setup;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.util.ClassUtils;

import com.atlassian.bamboo.configuration.InetAddressBean;
import com.atlassian.bamboo.license.BambooLicenseManager;
import com.atlassian.bamboo.upgrade.BootstrapUpgradeManager;
import com.atlassian.bamboo.util.BuildUtils;
import com.atlassian.bamboo.utils.Comparators;
import com.atlassian.config.ConfigurationException;
import com.atlassian.config.bootstrap.BootstrapException;
import com.atlassian.config.bootstrap.DefaultAtlassianBootstrapManager;
import com.atlassian.config.db.DatabaseDetails;
import com.atlassian.config.util.BootstrapUtils;
import com.atlassian.extras.api.bamboo.BambooLicense;
import com.atlassian.security.random.DefaultSecureRandomService;

@SuppressWarnings("unused")
public class DefaultBootstrapManager extends DefaultAtlassianBootstrapManager
		implements BootstrapManager {
	private static final String PROPERTY_INDEX_DIR = "lucene.index.dir";
	static final String CONFIG_DIRECTORY_PARAM = "bamboo.config.directory";
	private static final String BUILD_DIRECTORY_PARAM = "bamboo.project.directory";
	private static final String BROKER_URI = "bamboo.jms.broker.uri";
	private static final String BROKER_CLIENT_URI = "bamboo.jms.broker.client.uri";
	public static final String BUILD_WORKING_DIR = "buildWorkingDir";
	public static final String ARTIFACTS_DIR = "bamboo.artifacts.directory";
	private static final String JDBC_DRIVER_CLASS_MYSQL = "com.mysql.jdbc.Driver";
	private static final String fingerprint = String
			.valueOf(DefaultSecureRandomService.getInstance().nextLong());
	private BootstrapUpgradeManager bootstrapUpgradeManager;
	private static final String DEFAULT_BROKER_BIND_ADDRESS = "tcp://0.0.0.0:54663";
	private static final String DEFAULT_BROKER_CONNECTION_ADDRESS = "tcp://"
			+ InetAddressBean.getLocalCanonicalHostName("localhost") + ":54663";

	protected void afterConfigurationLoaded() throws ConfigurationException {
		if (!this.applicationConfig.configFileExists()) {
			this.applicationConfig.setBuildNumber(BuildUtils
					.getCurrentBuildNumber());
		}

		String homeDirBuildNumber = this.applicationConfig.getBuildNumber();
		String applicationBuildNumber = BuildUtils.getCurrentBuildNumber();
		if (Comparators.getApplicationBuildNumberComparator().compare(
				homeDirBuildNumber, applicationBuildNumber) > 0) {
			throw new ConfigurationException(
					"Your build of Bamboo ("
							+ applicationBuildNumber
							+ ") is too old to work with the data in your Bamboo home directory ("
							+ this.applicationConfig.getApplicationHome()
							+ "), which comes from build number "
							+ homeDirBuildNumber);
		}

		try {
			getBrokerClientURI();
			getBrokerURI();
		} catch (URISyntaxException exception) {
			throw new ConfigurationException("Could not configure broker URIs",
					exception);
		}
	}

	public String getConfigDirectory() {
		return (String) getProperty("bamboo.config.directory");
	}

	public void setConfigDirectory(String configurationDirectory) {
		setPropertyWithReplacement("bamboo.config.directory",
				configurationDirectory);
	}

	public String getBuildWorkingDirectory() {
		return (String) getProperty("buildWorkingDir");
	}

	public void setBuildWorkingDirectory(String buildWorkingDirectory) {
		setPropertyWithReplacement("buildWorkingDir", buildWorkingDirectory);
	}

	public void setArtifactsDirectory(String artifactsDir) {
		setPropertyWithReplacement("bamboo.artifacts.directory", artifactsDir);
	}

	public String getArtifactsDirectory() {
		return (String) getProperty("bamboo.artifacts.directory");
	}

	@Nullable
	public File getIndexDirectory() {
		String indexPath = (String) getProperty("lucene.index.dir");
		if (indexPath != null) {
			return new File(indexPath);
		}

		return null;
	}

	public String getBuildDirectory() {
		return (String) getProperty("bamboo.project.directory");
	}

	public void setBuildDirectory(String buildDirectory) {
		setPropertyWithReplacement("bamboo.project.directory", buildDirectory);
	}

	void setPropertyWithReplacement(@NotNull String key, @Nullable Object value) {
		if ((value instanceof String)) {
			value = StringUtils.replace((String) value, getApplicationHome(),
					"${bambooHome}");
		}

		setProperty(key, value);
	}

	public URI getBrokerURI() throws URISyntaxException {
		String brokerURIString = System.getProperty("bamboo.jms.broker.uri",
				(String) getProperty("bamboo.jms.broker.uri"));
		boolean blank = StringUtils.isBlank(brokerURIString);
		if (blank) {
			brokerURIString = "tcp://0.0.0.0:54663";
		}
		URI brokerURI = new URI(brokerURIString);
		if (blank) {
			setBrokerURI(brokerURI);
		}
		return BrokerURIUtils.decorateBrokerURI(brokerURI);
	}

	public void setBrokerURI(URI brokerURI) {
		setProperty("bamboo.jms.broker.uri", brokerURI.toString());
	}

	public URI getBrokerClientURI() throws URISyntaxException {
		String brokerClientURIString = System.getProperty(
				"bamboo.jms.broker.client.uri",
				(String) getProperty("bamboo.jms.broker.client.uri"));
		boolean blank = StringUtils.isBlank(brokerClientURIString);
		if (blank) {
			String brokerConnectionAddress = determineBrokerConnectionAddress();
			String brokerConnectionUri = BrokerURIUtils.decorateBrokerURI(
					new URI(brokerConnectionAddress)).toString();

			brokerClientURIString = "failover:(" + brokerConnectionUri + ")?"
					+ "maxReconnectAttempts=10&initialReconnectDelay=15000";
		}

		URI brokerClientURI = new URI(brokerClientURIString);
		if (blank) {
			setBrokerClientURI(brokerClientURI);
		}
		return BrokerURIUtils.decorateBrokerURI(brokerClientURI);
	}

	private String determineBrokerConnectionAddress() {
		boolean isRealHostAddress = false;
		URI brokerUri = null;
		try {
			brokerUri = getBrokerURI();

			InetAddress address = InetAddress.getByName(brokerUri.getHost());

			isRealHostAddress = (!address.isAnyLocalAddress())
					&& (!address.isMulticastAddress())
					&& (address.isReachable(100));
		} catch (UnknownHostException ignored) {
		} catch (IOException ignored) {
		} catch (URISyntaxException ignored) {
		}
		return isRealHostAddress ? brokerUri.getScheme() + "://"
				+ brokerUri.getAuthority() : DEFAULT_BROKER_CONNECTION_ADDRESS;
	}

	public void setBrokerClientURI(URI brokerURI) {
		setProperty("bamboo.jms.broker.client.uri", brokerURI.toString());
	}

	protected boolean performPersistenceUpgrade() {
		BambooLicenseManager licenseManager = (BambooLicenseManager) BootstrapUtils
				.getBootstrapContext().getBean("bambooLicenseManager");
		BambooLicense license = licenseManager.getLicense();
		if (license != null) {
			if (licenseManager.isMaintenanceExpiredForBuild()) {
				log.fatal("Unable to run pre-bootstrap upgrade tasks as your support period has ended");
				this.bootstrapFailureReason = "Unable to run pre-bootstrap upgrade tasks as your support period has ended. Please contact Atlassian and https://support.atlassian.com";
				return false;
			}

		}

		String datasourceName = (String) getProperty("hibernate.connection.datasource");
		if (datasourceName == null) {
			String driverClass = (String) getProperty("hibernate.connection.driver_class");
			if (!ClassUtils.isPresent(driverClass)) {
				this.bootstrapFailureReason = ("Unable to find JDBC Driver class '"
						+ driverClass + "'. \n" + "Please ensure the JAR is present in the WEB-INF/lib directory of your Bamboo installation or consult our documentation at https://confluence.atlassian.com/display/BAMBOO/Connecting+Bamboo+to+an+external+database.");

				if ("com.mysql.jdbc.Driver".equals(driverClass)) {
					this.bootstrapFailureReason += " \nPlease note that the MySQL JDBC Driver has been removed from Bamboo 4.3 due to licensing restrictions. ";
				}

				log.fatal(this.bootstrapFailureReason);
				return false;
			}
		}

		boolean upgradeSucceeded = false;
		try {
			log.info("Running pre-bootstrap validation tasks");
			List validationErrorMessages = this.bootstrapUpgradeManager
					.runValidationTests();
			upgradeSucceeded = validationErrorMessages.isEmpty();
			if (!upgradeSucceeded) {
				log.fatal("Pre-bootstrap validation tests failed: "
						+ validationErrorMessages.toString());
			} else {
				log.info("Running pre-bootstrap upgrade tasks");
				List errorMessages = this.bootstrapUpgradeManager
						.doUpgrade(false);
				upgradeSucceeded = errorMessages.isEmpty();
				if (!upgradeSucceeded) {
					log.fatal("Pre-bootstrap upgrade tasks failed: "
							+ errorMessages.toString());
				}
			}
		} catch (Exception e) {
			log.fatal("Pre-bootstrap upgrade tasks failed", e);
		}

		if (!upgradeSucceeded) {
			this.bootstrapFailureReason = "Failed to run pre-bootstrap upgrade tasks. Please contact Atlassian and https://support.atlassian.com";
		}

		return upgradeSucceeded;
	}

	public void bootstrapDatabase(DatabaseDetails databaseDetails, boolean b)
			throws BootstrapException {
		super.bootstrapDatabase(databaseDetails, b);
	}

	public void bootstrapDatasource(String datasourceName,
			String hibernateDialect) throws BootstrapException {
		super.bootstrapDatasource(datasourceName, hibernateDialect);
	}

	protected String getDbUrl(DatabaseDetails dbDetails) {
		String dbDetailsDbUrl = super.getDbUrl(dbDetails);
		return StringUtils.replace(dbDetailsDbUrl, "${bambooHome}",
				getApplicationHome());
	}

	public String getServerID() {
//		return (String) getProperty("serverId");
		return "BTTV-5QO2-OXIZ-49MQ";
	}

	public void setServerID(String serverId) {
		setProperty("serverId", serverId);
	}

	public int getHeartbeatInterval() {
		Object value = getProperty("bamboo.agent.heartbeatInterval");
		if ((value instanceof String)) {
			try {
				return Integer.parseInt((String) value);
			} catch (NumberFormatException exception) {
			}
		}

		return 5;
	}

	public void setHeartbeatInterval(Integer heartbeartInterval) {
		setProperty(
				"bamboo.agent.heartbeatInterval",
				heartbeartInterval == null ? null : heartbeartInterval
						.toString());
	}

	@NotNull
	public String getFingerprint() {
		return fingerprint;
	}

	public void setBootstrapUpgradeManager(
			BootstrapUpgradeManager bootstrapUpgradeManager) {
		this.bootstrapUpgradeManager = bootstrapUpgradeManager;
	}
}